using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameFramework;
using UnityGameFramework.Runtime;
using GUtility = GameFramework.Utility;
using System;
using LS.Tools;

namespace LS.Entity
{
    public abstract class EntityBase : EntityLogic
    {
        /// <summary>
        /// 实体初始化事件
        /// </summary>
        public event Action<EntityBase, object> OnEntityInit;
        
        /// <summary>
        /// 实体显示事件
        /// </summary>
        public event Action<EntityBase, object> OnEntityShow;
        
        /// <summary>
        /// 实体隐藏事件
        /// </summary>
        public event Action<EntityBase, bool, object> OnEntityHide;
        
        /// <summary>
        /// 实体附加子实体
        /// </summary>
        public event Action<EntityBase, EntityLogic, Transform, object> OnEntityAttached;
        
        /// <summary>
        /// 实体解除子实体
        /// </summary>
        public event Action<EntityBase, EntityLogic, object> OnEntityDetached;
        
        /// <summary>
        /// 实体附加子实体
        /// </summary>
        public event Action<EntityBase,EntityLogic, Transform, object> OnEntityAttachedTo;
        
        /// <summary>
        /// 实体解除子实体
        /// </summary>
        public event Action<EntityBase,EntityLogic, object> OnEntityDetachedFrom;
        
        /// <summary>
        /// 实体更新事件
        /// </summary>
        public event Action<EntityBase, float, float> OnEntityUpdate;
        
        /// <summary>
        /// 实体可见性改变时事件
        /// </summary>
        public event Action<EntityBase, bool> OnEntityVisableChanged;
        
        /// <summary>
        /// 实体回收时事件
        /// </summary>
        public event Action<EntityBase> OnEntityRecycle;
        
        [SerializeField]
        private int m_EntityTypeId = 0;
        
        [SerializeField]
        private EntityParams m_EntityData = null;

        [SerializeField]
        private EntityLogic m_Parent = null;

        private FrameInvoker m_Invoker;

        private Guid m_Guid;
        
        public int EntityTypeId
        {
            get
            {
                return m_EntityTypeId;
            }
        }
        
        public int SerialId
        {
            get
            {
                return Entity.Id;
            }
        }

        public Guid Guid
        {
            get
            {
                return m_Guid;
            }
        }
        
        public Animation CachedAnimation
        {
            get;
            private set;
        }

        public EntityLogic Parent
        {
            get
            {
                return m_Parent;
            }
        }

        public FrameInvoker Invoker
        {
            get
            {
                if (m_Invoker == null)
                {
                    m_Invoker = ReferencePool.Acquire<FrameInvoker>();
                }
                return m_Invoker;
            }
        }

        protected override void OnInit(object userData)
        {
            base.OnInit(userData);
            CachedAnimation = GetComponent<Animation>();
            this.OnEntityInit?.Invoke(this, userData);
        }

        protected override void OnRecycle()
        {
            this.OnEntityRecycle?.Invoke(this);
            base.OnRecycle();
        }

        protected override void OnShow(object userData)
        {
            base.OnShow(userData);
            var entityData = userData as EntityParams;
            if (entityData == null)
            {
                Log.Error("Entity data is invalid.");
            }
            else
            {
                m_EntityTypeId =  entityData.EntityTypeId;
                Name = GUtility.Text.Format("[Entity {0}]", SerialId.ToString());
                CachedTransform.localPosition = entityData.Position;
                CachedTransform.localRotation = entityData.Rotation;
                CachedTransform.localScale = entityData.Scale;
                if (entityData.SpecialGuid.HasValue)
                    this.m_Guid = entityData.SpecialGuid.Value;
                else
                    this.m_Guid = Guid.NewGuid();
            }
            this.OnEntityShow?.Invoke(this, userData);
        }

        protected override void OnHide(bool isShutdown, object userData)
        {
            if (m_Invoker != null)
            {
                ReferencePool.Release(m_Invoker);
            }
            m_Invoker = null;
            this.OnEntityHide?.Invoke(this, isShutdown, userData);
            base.OnHide(isShutdown, userData);
        }

        protected override void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
        {
            base.OnAttached(childEntity, parentTransform, userData);
            this.OnEntityAttached?.Invoke(this, childEntity, parentTransform, userData);
        }

        protected override void OnDetached(EntityLogic childEntity, object userData)
        {
            this.OnEntityDetached?.Invoke(this, childEntity, userData);
            base.OnDetached(childEntity, userData);
        }

        protected override void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
        {
            base.OnAttachTo(parentEntity, parentTransform, userData);
            this.m_Parent = parentEntity;
            this.OnEntityAttachedTo?.Invoke(this, parentEntity, parentTransform, userData);
        }

        protected override void OnDetachFrom(EntityLogic parentEntity, object userData)
        {
            this.m_Parent = null;
            this.OnEntityDetachedFrom?.Invoke(this, parentEntity, userData);
            base.OnDetachFrom(parentEntity, userData);
        }

        protected override void OnUpdate(float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(elapseSeconds, realElapseSeconds);
            m_Invoker?.UpdateInvoke();
            this.OnEntityUpdate?.Invoke(this, elapseSeconds, realElapseSeconds);
        }

        protected override void InternalSetVisible(bool visible)
        {
            bool previousActive = gameObject.activeSelf;
            base.InternalSetVisible(visible);
            if (previousActive != visible)
            {
                this.OnEntityVisableChanged?.Invoke(this, visible);
            }
        }
    }
}
